import { useRef, forwardRef, useImperativeHandle, useEffect, type ReactNode } from 'react';
import * as echarts from 'echarts';
import type { ECharts, EChartsOption, RegisteredSeriesOption } from 'echarts';
import ChartEmptyImage from '@/assets/chart_empty.svg';
import { debounce } from 'lodash';
import { Typography } from 'antd';
import { motion } from 'framer-motion';

export interface seriesDataArrayItem {
  name: string; // 图形名称
  data: any[]; // 图形数据
  type: RegisteredSeriesOption[keyof RegisteredSeriesOption]; // 图形类型
  chartColor?: any; // 图形颜色，可以是渐变色
  tooltipColor?: string; // tooltip提示框颜色
  valueMax?: number | ((dataItem: any) => number); // y轴最大值
  valueMin?: number | ((dataItem: any) => number); // y轴最小值F
  unit?: string; //单位
  labelColor?: string; //标签字体颜色
  labelFontSize?: number; //标签字体大小
  labelProps?: any; //标签配置
  lineShadowColor?: string; //线条阴影颜色
  tooltipName?: string; //tooltip提示框名称
  hideSeriesLabelUnit?: boolean; //是否隐藏series的label单位
  yAxisConfig?: EChartsOption['yAxis']; //y轴配置
  labelPosition?:
  | 'top'
  | 'left'
  | 'right'
  | 'bottom'
  | 'inside'
  | 'insideLeft'
  | 'insideRight'
  | 'insideTop'
  | 'insideBottom'
  | 'insideTopLeft'
  | 'insideBottomLeft'
  | 'insideTopRight'
  | 'insideBottomRight'
  | (number | string)[];

  [key: string]: any; //其他属性
}

export interface OptionsType {
  xAxisData?: any[];
  yAxisData?: any[];
  xAxisConfig?: any; //单x轴配置
  yAxisConfig?: any; //单y轴配置
  xAxisProps?: EChartsOption['xAxis']; //x轴配置
  yAxisProps?: EChartsOption['yAxis']; //y轴配置
  dataZoom?: any; //缩放
  gridProps?: EChartsOption['grid']; //网格配置
  gridPosition?: string[]; //对应上、右、下、左的位置，相当于css中的absolute定位
  legendProps?: EChartsOption['legend']; //图例配置
  tooltipProps?: EChartsOption['tooltip']; //提示框配置
  tooltipTriggerType?: 'axis' | 'item' | 'none'; //提示框触发类型
  tooltipFormatter?: (params: any) => string; //tooltip格式化
  visualMap?: EChartsOption['visualMap']; //视觉映射组件
  //支持多个series传入（在元素中展示多个图形），每个series对应一个图形
  seriesDataArray: seriesDataArrayItem[];
  axisPointer?: EChartsOption['axisPointer']; //坐标轴指示器
}

interface ChartElementProps extends React.HTMLAttributes<HTMLDivElement> {
  options: OptionsType;
  height?: number | string; //图表高度
  axisInverse?: boolean; //是否反向坐标轴
  emptyDescription?: string; //无数据时的描述
  emptyRender?: ReactNode; //空数据渲染
}

export type ChartElementRef = {
  chartInstance: ECharts | null | undefined;
};

const ChartElement = forwardRef<ChartElementRef, ChartElementProps>(
  ({ options, height, axisInverse = false, emptyDescription, emptyRender, ...rest }, ref) => {
    const chartRef = useRef<HTMLDivElement>(null);
    const chartInstance = useRef<ECharts>();

    useImperativeHandle(ref, () => ({
      chartInstance: chartInstance.current,
    }));

    //初始化图表配置
    const getInitalChartOptions = ({
      xAxisData,
      yAxisData,
      xAxisConfig = {},
      yAxisConfig = {},
      tooltipProps,
      tooltipTriggerType,
      yAxisProps,
      xAxisProps,
      dataZoom,
      seriesDataArray,
      gridProps,
      gridPosition = [],
      legendProps,
      visualMap,
      tooltipFormatter,
      axisPointer,
    }: OptionsType): EChartsOption => {
      try {
        const initialOptions: EChartsOption = {
          legend: legendProps || {
            show: true,
            left: 'center',
            top: 0,
            textStyle: {
              fontWeight: 'bold',
              fontSize: 12,
            },
          },
          tooltip: tooltipProps || {
            trigger: tooltipTriggerType || 'axis',
            backgroundColor: 'rgba(0,0,0,0.5)',
            textStyle: {
              color: '#fff',
            },
            borderColor: '#2692ae',
            borderWidth: 0,
            axisPointer: {
              type: 'cross',
              label: {
                backgroundColor: '#6a7985',
              },
            },
            formatter: tooltipFormatter
              ? tooltipFormatter
              : function (params: any) {
                if (Array.isArray(params)) {
                  const seriesDataArrayToString = seriesDataArray.reduce(
                    (acc, item, currentIndex) => {
                      const currentItem = params[currentIndex] || {};
                      if (currentItem.value === undefined || currentItem.value === null)
                        return acc;
                      return (
                        acc +
                        `<div style="color: #7ec7ff;"><span style="color:#fff;;font-size: 14px;">
                        <span style="display:inline-block;margin-right:5px;border-radius:25px;width:10px;height:10px;background-color:${item.tooltipColor
                        };"></span>
                        ${item.tooltipName || item.name}：<span>${currentItem.value + (item.unit || '')
                        }
                      </div>`
                      );
                    },
                    '',
                  );

                  if (!seriesDataArrayToString) return '';

                  return `<div style="padding: 5px 10px;">
                  <div style="color: #7ec7ff;font-size: 14px;font-weight:bold;">
                  ${params[0]?.name}
                  </div>
                 ${seriesDataArrayToString}
                </div>`;
                } else {
                  const currentItem = seriesDataArray[0] || {};
                  let content = '';
                  if (params.seriesType === 'heatmap') {
                    content = `${params.data[1]}: ${params.data[2]}`;
                  } else {
                    content = `${currentItem.tooltipName || currentItem.name}：${params.value + (currentItem.unit || '')
                      }`;
                  }

                  return `<div style="padding: 5px 10px;">
                  <div style="color: #7ec7ff;font-size: 14px;font-weight:bold;">
                  ${params.name}
                  </div>
                <div style="color: #7ec7ff;"><span style="color:#fff;;font-size: 14px;">
                  <span style="display:inline-block;margin-right:5px;border-radius:25px;width:10px;height:10px;background-color:${currentItem.tooltipColor || '#1890ff'
                    };"></span>
                  ${content}
                 </div>`;
                }
              },
          },
          xAxis: {
            boundaryGap: true,
            nameLocation: 'end',
            data: xAxisData,
            axisLabel: {
              color: '#000',
              fontSize: 12,
              showMaxLabel: true,
            },
            axisLine: {
              lineStyle: {
                color: 'rgba(210, 220, 220, 0.15)',
              },
            },
            ...xAxisConfig,
          },
          grid: gridProps || {
            top: gridPosition[0] || '0%',
            right: gridPosition[1] || '0%',
            bottom: gridPosition[2] || '0%',
            left: gridPosition[3] || '0%',
            containLabel: true,
          },
          series: seriesDataArray.map((item, seriesDataIndex) => {
            const {
              name,
              data,
              type,
              chartColor,
              unit,
              lineShadowColor,
              labelFontSize,
              labelProps = {},
              hideSeriesLabelUnit = false,
              ...restItem
            } = item;
            let series: any = {
              name: name,
              data: data,
              type,
              label: {
                show: true,
                position: item.labelPosition || 'top',
                fontWeight: 'bold',
                color: item.labelColor,
                fontSize: labelFontSize || (12),
                formatter: function (params: any) {
                  return params.value + (!hideSeriesLabelUnit && unit ? unit : '');
                },
                ...labelProps,
              },
              ...restItem,
            };
            if (type === 'bar') {
              series = {
                type: 'bar',
                emphasis: {
                  disabled: true,
                },
                barMaxWidth: 30,
                barGap: '10%',
                showBackground: true,
                backgroundStyle: {
                  color: 'rgba(210, 220, 220, 0.15)',
                },
                itemStyle: {
                  color: chartColor,
                },
                ...series,
              };
            } else if (type === 'line') {
              series = {
                smooth: false,
                symbolSize: 4,
                itemStyle: {
                  color: chartColor,
                },
                type: 'line',
                zlevel: 3,
                lineStyle: {
                  color: chartColor || 'rgba(0,144,255,1)',
                  shadowBlur: 3,
                  shadowColor: lineShadowColor || 'rgba(0,144,255,0.5)',
                  shadowOffsetY: 8,
                },
                yAxisIndex: seriesDataIndex,
                ...series,
              };
            } else if (type === 'heatmap') {
              series = {
                ...series,
                label: {
                  show: true,
                  ...labelProps,
                },
              };
            }
            return series;
          }),
          dataZoom: dataZoom,
          visualMap,
          axisPointer,
        };
        if (!axisInverse) {
          if (xAxisProps) initialOptions.xAxis = xAxisProps;
          else
            initialOptions.xAxis = {
              type: 'category',
              ...initialOptions.xAxis,
            };
          if (yAxisProps) initialOptions.yAxis = yAxisProps;
          else
            initialOptions.yAxis = seriesDataArray.map((item) => ({
              splitLine: {
                lineStyle: {
                  type: 'dashed',
                },
              },
              max: item.valueMax,
              min: item.valueMin,
              axisLine: {
                show: true,
                lineStyle: {
                  color: 'rgba(210, 220, 220, 0.15)',
                },
              },
              // interval: item.valueMax ? item.valueMax / 5 : null,
              axisLabel: {
                color: '#000',
                fontSize: 12,
                formatter: `{value}${item.unit || ''}`,
              },
              axisTick: {
                show: false,
              },
              ...yAxisConfig,
              ...item.yAxisConfig,
            }));
        } else {
          initialOptions.yAxis = {
            type: 'category',
            splitLine: {
              lineStyle: {
                type: 'dashed',
              },
            },
            inverse: true,
            axisLine: {
              show: true,
              lineStyle: {
                color: 'rgba(210, 220, 220, 0.15)',
              },
            },
            data: yAxisData,
            // interval: item.valueMax ? item.valueMax / 5 : null,
            axisLabel: {
              color: '#000',
              fontSize: 12,
              showMaxLabel: true,
              // formatter: `{value}${item.unit || ''}`,
            },
            axisTick: {
              show: false,
            },
            ...yAxisConfig,
          };
        }
        return initialOptions;
      } catch (error) {
        console.warn('getInitalChartOptions error:', error);
        return {};
      }
    };

    useEffect(() => {
      if (options.seriesDataArray.length < 1 && chartInstance.current) {
        chartInstance.current.clear();
        chartInstance.current.dispose();
        chartInstance.current = undefined;
      }
      if (chartRef.current && !chartInstance.current) {
        chartInstance.current = echarts.init(chartRef.current);
      }
      if (chartInstance.current) {
        chartInstance.current.setOption(getInitalChartOptions(options), false, true);
      }
      const resize = debounce(() => {
        chartInstance.current && chartInstance.current.resize();
      }, 10);
      window.addEventListener('resize', resize);
      const chart = chartRef.current;
      const observer = new ResizeObserver(resize);
      chart && observer.observe(chart);
      return () => {
        window.removeEventListener('resize', resize);
        observer.disconnect();
        chart && observer.unobserve(chart);
      };
    }, [chartRef, options]);

    return (
      <>
        {options.seriesDataArray.length ? (
          <div ref={chartRef} style={{ height: height || 350 }} {...rest} />
        ) : null}
        {options.seriesDataArray.length < 1 && (
          <motion.div
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
            transition={{ duration: 0.5 }}
            key="empty"
          >
            {emptyRender || (
              <div className="flex flex-col items-center">
                <img src={ChartEmptyImage} alt="charts" style={{ height: 350 }} />
                <Typography.Text
                  type="secondary"
                  className="font-weight-bold"
                  style={{ color: '#1890ff' }}
                >
                  暂无展示数据
                </Typography.Text>
                {emptyDescription ? (
                  <div
                    className="mt-12"
                    style={{
                      fontSize: 12,
                      color: '#748194',
                    }}
                  >
                    {emptyDescription}
                  </div>
                ) : null}
              </div>
            )}
          </motion.div>
        )}
      </>
    );
  },
);

export default ChartElement;
